home *** CD-ROM | disk | FTP | other *** search
/ Aminet 5 / Aminet 5 - March 1995.iso / Aminet / text / misc / pcal_4_5.lha / pcal / writefil.c < prev   
C/C++ Source or Header  |  1994-10-16  |  37KB  |  1,425 lines

  1. /*
  2.  * writefil.c - Pcal routines concerned with writing the PostScript output
  3.  *
  4.  * Contents:
  5.  *
  6.  *        def_footstring
  7.  *        expand_fmt
  8.  *        find_daytext
  9.  *        find_noteboxes
  10.  *        print_colors
  11.  *        print_dates
  12.  *        print_db_word
  13.  *        print_julian_info
  14.  *        print_month
  15.  *        print_moon_info
  16.  *        print_pstext
  17.  *        print_text
  18.  *        print_word
  19.  *        select_color
  20.  *        set_rgb
  21.  *        write_calfile
  22.  *        write_psfile
  23.  *
  24.  * Revision history:
  25.  *
  26.  *    4.5    AWR    04/05/94    select real vs. dummy PostScript code
  27.  *                    within write_psfile() (cf. pcalinit.ps)
  28.  *
  29.  *            11/30/93    pre-scale all fonts used (as suggested
  30.  *                    by Andrew Houghton; cf. pcalinit.ps)
  31.  *
  32.  *            11/24/93    replace find_holidays() with
  33.  *                    print_colors() (cf. drawnums{} in
  34.  *                    pcalinit.ps)
  35.  *
  36.  *            11/16/93    Add set_rgb() to handle red:green:blue
  37.  *                    values for date/fill colors
  38.  *
  39.  *            09/23/93    Support both ROMAN-8 and LATIN-8 font
  40.  *                    mappings
  41.  *
  42.  *        AWR    07/09/93    Revised PostScript comment block
  43.  *
  44.  *        AWR    03/01/93    add optional mapping of 8-bit fonts
  45.  *
  46.  *        AWR    02/05/93    Support -# flag (multiple copies of
  47.  *                    each output page)
  48.  *
  49.  *        AWR    04/22/92    use STRSIZ for temp buffer size
  50.  *
  51.  *    4.4    AWR    04/07/92    revise to use new PUTCHAR and PUTSTR
  52.  *                    macros (cf. pcaldefs.h)
  53.  *
  54.  *            01/20/92    support -z and revised -[bgGO] flags
  55.  *
  56.  *        AWR    01/13/92    support optional font size in -d and
  57.  *                    -t flags; move initialization of fonts
  58.  *                    and sizes here (from pcalinit.ps)
  59.  *
  60.  *    4.3    AWR    12/03/91    add support for -s flag (specify
  61.  *                    alternate date/fill box shading values)
  62.  *
  63.  *    4.2    AWR    10/08/91    add support for -[kK] flags (change
  64.  *                    position of small calendars)
  65.  *
  66.  *            10/03/91    add find_noteboxes(); revise to print
  67.  *                    text in multiple notes boxes
  68.  *
  69.  *                    add support for -S flag
  70.  *
  71.  *            10/02/91    modify def_footstring() to handle all
  72.  *                    types of strings; use it to print notes
  73.  *                    header (-N flag)
  74.  *
  75.  *            09/19/91    add write_calfile(), print_dates(),
  76.  *                    and new print_text() to generate 
  77.  *                    input for Un*x "calendar" utility;
  78.  *                    renamed old print_text() as
  79.  *                    print_pstext() for clarity; revised
  80.  *                    to simplify setting working date
  81.  *
  82.  *    4.11    AWR    08/23/91    revise expand_fmt() to write results
  83.  *                    to string instead of stdout; revise
  84.  *                    print_word() to avoid writing null
  85.  *                    strings
  86.  *
  87.  *        AWR    08/21/91    use ABBR_DAY_LEN and ABBR_MONTH_LEN
  88.  *                    (cf. pcallang.h) to print abbreviated
  89.  *                    day/month names
  90.  *
  91.  *        AWR    08/21/91    add %u and %w (calculate week number
  92.  *                    so that 1/1 is always week 1); support
  93.  *                    %[+-]<n>[DWMY] to adjust working date
  94.  *                    by +|- <n> days/weeks/months/years
  95.  *
  96.  *    4.1    AWR    08/16/91    Support -G flag (outlined gray dates)
  97.  *
  98.  *    4.02    AWR    07/02/91    Added "%" expansions in text strings
  99.  *                    (cf. expand_fmt())
  100.  *
  101.  *    4.0    AWR    01/28/91    Support -B, -w flags and moon file
  102.  *
  103.  *            01/15/91    Extracted from pcal.c
  104.  *
  105.  */
  106.  
  107. /*
  108.  * Standard headers:
  109.  */
  110.  
  111. #include <stdio.h>
  112. #include <ctype.h>
  113. #include <string.h>
  114. #include <time.h>
  115.  
  116. /*
  117.  * Pcal-specific definitions:
  118.  */
  119.  
  120. #include "pcaldefs.h"
  121. #include "pcalglob.h"
  122. #define  WRITEFIL        /* to get ordinal_suffix() from pcallang.h */
  123. #include "pcallang.h"
  124. #include "pcalinit.h"        /* PostScript boilerplate */
  125.  
  126. /*
  127.  * Macros:
  128.  */
  129.  
  130. /* make sure PRT() doesn't round "ph" up to 1.0 when printing it */
  131. #define PRT_TWEAK(ph)        ((ph) >= 0.9995 ? 0.0 : (ph))
  132.  
  133. /* advance working date by n days */
  134. #define SET_DATE(n)    do {                        \
  135.     MAKE_DATE(date, work_month, work_day + (n), work_year);        \
  136.     normalize(&date);                        \
  137.     work_month = date.mm, work_day = date.dd, work_year = date.yy;    \
  138. } while (0)
  139.  
  140. /* prescale a font and add its name to list */
  141. #define ADDFONT(name, size, font, isarray)    do {            \
  142.     char *p = alloc(strlen(name) + strlen(size) + 2);        \
  143.     sprintf(allfonts[nfonts++] = p, "%s_%s", name, size);        \
  144.     PRT("/%s { %sfontsize ", p, font);                \
  145.     if (isarray) PRT("%s get ", size);                \
  146.     PRT("%sfont FontFind } def\n", font);                \
  147. } while (0)
  148.  
  149. #define GEN_PSCODE(arr)                            \
  150.     for (ap = arr; *ap; ap++)                    \
  151.         PRT("%s\n", *ap)
  152.  
  153. #define FOOTSTRINGS()    (lfoot[0] || cfoot[0] || rfoot[0])
  154.  
  155. /* various PostScript comments and commands for start/end of page */
  156.  
  157. #ifdef EPS
  158.  
  159. /* macro definitions for generating EPS output */
  160.  
  161. #define PS_STARTPAGE()    do {                        \
  162.     page++;                                \
  163.     PRT("%%%%Page: %d %d\n", page, page);                \
  164.     PRT("%%%%BeginPageSetup\n");                    \
  165.     PRT("clear flush\n");                        \
  166.     PRT("/PageNum { %d } def\n", page);                \
  167.     PRT("/PageState save def\n");                    \
  168.     PRT("%%%%EndPageSetup\n");                    \
  169. } while (0)
  170.  
  171. #define PS_ENDPAGE()    do {                        \
  172.     PRT("%%%%PageTrailer\n");                    \
  173.     PRT("showpage\n");                        \
  174.     PRT("clear flush\n");                        \
  175.     PRT("PageState restore\n");                    \
  176. } while (0)
  177.  
  178. #define PS_TRAILER()    do {                        \
  179.     PRT("%%%%Trailer\n");                        \
  180.     PRT("clear flush\n");                        \
  181.     PRT("%%%%EOF\n");                        \
  182. } while (0)
  183.  
  184. #else /* non-EPS flavors of above macros */
  185. #define PS_STARTPAGE()
  186. #define PS_ENDPAGE()        PRT("showpage\n");
  187. #define PS_TRAILER()
  188. #endif
  189.  
  190. /* reset working date to original date */
  191. #define RESET_DATE()    \
  192. work_month = this_month, work_day = this_day, work_year = this_year
  193.  
  194. #define NEWFONT        "-8"    /* suffix for new 8-bit fonts */
  195.  
  196. #define MAXFONT        20    /* maximum number of fonts to prescale */
  197.  
  198. /*
  199.  * Globals:
  200.  */
  201.  
  202. /* order of following strings must conform to #define's in pcaldefs.h (q.v.) */
  203. static char *cond[3] = {"false", "true", "(some)"};
  204.  
  205. static int this_day, this_month, this_year;    /* current day */
  206. static int work_day, work_month, work_year;    /* working day (cf. expand_fmt()) */
  207. static char *kw_note, *kw_opt, *kw_year;    /* keywords for -c output */
  208.  
  209. static int debug_text;                /* generate debug output */
  210.  
  211.  
  212. /*
  213.  * write_psfile - write PostScript code
  214.  *
  215.  * The actual output of the PostScript code is straightforward.  This routine
  216.  * writes a PostScript header followed by declarations of all the PostScript
  217.  * variables affected by command-line flags and/or language dependencies.  It
  218.  * the generates the PostScript boilerplate generated from pcalinit.ps, and
  219.  * finally calls print_month() to generate the PostScript code for each
  220.  * requested month.
  221.  *
  222.  */
  223. void
  224. #ifdef PROTOS
  225. write_psfile(int month,            /* starting month   */
  226.          int year,            /* starting year    */
  227.          int nmonths)        /* number of months */
  228. #else
  229. write_psfile(month, year, nmonths)
  230.     int month;            /* starting month   */
  231.     int year;            /* starting year    */
  232.     int nmonths;            /* number of months */
  233. #endif
  234. {
  235.     int i, nfonts, nfsize, copies, dfltsize, color_dates;
  236.     char *p, **ap, tmp[STRSIZ], alt_color, *allfonts[MAXFONT];
  237.     struct tm *p_tm;
  238.     time_t t;
  239. #ifdef UN_X
  240.     struct passwd *pw;
  241. #endif
  242.     /* default date, title, weekday font sizes (small/medium/large) */
  243.     static int dsize[3] = DATEFONTSIZE;
  244.     static int tsize[3] = TITLEFONTSIZE;
  245.     static int wsize[3] = WEEKDAYFONTSIZE;
  246.     static int fsize[3] = FOOTFONTSIZE;
  247.     static int dmargin[3] = DATEMARGIN;
  248.     static double gwidth[3] = GRIDLINEWIDTH;
  249.     static char *calsize[3] = CALSIZE;
  250.  
  251.     debug_text = DEBUG(DEBUG_TEXT);        /* debug text output? */
  252.  
  253.     /*
  254.      * Write out PostScript prolog (including version/datefile stamp)
  255.      */
  256.  
  257.     /* comment block at top */
  258.  
  259.      PRT("%%!%s\n", PS_RELEASE);        /* PostScript release */
  260.  
  261.  
  262.     PRT("%%%%CreationDate: ");        /* date/time stamp */
  263.     p_tm = localtime((time(&t), &t));
  264.     if (date_style == EUR_DATES)
  265.         PRT("%d.%d.", p_tm->tm_mday, p_tm->tm_mon + 1);
  266.     else
  267.         PRT("%d/%d/", p_tm->tm_mon + 1, p_tm->tm_mday);
  268.     PRT("%02d %02d:%02d:%02d\n", p_tm->tm_year % 100, p_tm->tm_hour,
  269.         p_tm->tm_min, p_tm->tm_sec);
  270.     PRT("%%%%Creator: %s %s %s\n", CREATED_MSG, progname, version);
  271.  
  272. #ifdef UN_X
  273.     if ((pw = getpwuid(getuid())) != NULL) {
  274.         strcpy(tmp, pw->pw_gecos);
  275.         if ((p = strchr(tmp, ',')) != NULL)
  276.             *p = '\0';
  277.         PRT("%%%%For: %s\n", pw->pw_name);
  278.         PRT("%%%%Routing: %s\n", tmp);
  279.     }
  280. #endif
  281.  
  282.     /* Identify the output (month/year range and input file) */
  283.  
  284.     if (do_whole_year && month == JAN) {
  285.         PRT("%%%%Title: %s %d", TITLE_MSG, year);
  286.         if (nmonths > 12)
  287.             PRT(" - %d", year + ((nmonths - 1) / 12));
  288.     } else {
  289.         char c = date_style == EUR_DATES ? '.' : '/';
  290.         PRT("%%%%Title: %s %d%c%02d", TITLE_MSG, month, c, year % 100);
  291.         if (nmonths > 1)
  292.             PRT(" - %d%c%02d", (month + (nmonths - 1) - 1) % 12 + 1,
  293.                 c, (year + (nmonths - 1 + month - 1) / 12) % 100);
  294.     }
  295.     if (*datefile)
  296.         PRT(DATEFILE_MSG, datefile);
  297.     PRT("\n");
  298.  
  299.     /* Miscellaneous other identification */
  300.  
  301.     PRT("%%%%Pages: %d\n", do_whole_year ? nmonths / 12 : nmonths);
  302.     PRT("%%%%PageOrder: Ascend\n");
  303.     PRT("%%%%Orientation: %s\n", rotate == LANDSCAPE ? "Landscape" :
  304.                                "Portrait");
  305.     PRT("%%%%BoundingBox: 0 0 612 792\n");
  306.     PRT("%%%%ProofMode: NotifyMe\n");
  307.     PRT("%%%%EndComments\n\n");
  308.  
  309.     /* number of copies (from -#<n> flag) */
  310.  
  311.     if ((copies = atoi(ncopy)) > MAXCOPY)
  312.         copies = MAXCOPY;        /* save a tree */
  313.     if (copies > 1)
  314.         PRT("/#copies %d def\n", copies);
  315.  
  316.     /* calendar sizes: to minimize number of pre-scaled fonts, whole-
  317.      * year calendars define 'medium' as 0 and the other sizes as -1
  318.      * (not used); single-month calendars define 'large' as 0, 'small'
  319.      * as 1, and 'medium' as -1 (not used)
  320.      */
  321.  
  322.     for (i = SMALL; i <= LARGE; i++)
  323.         PRT("/%s %d def\n", calsize[i],
  324.             do_whole_year ? (i == MEDIUM ? 0 : -1) :
  325.                     (i == MEDIUM ? -1 : i == SMALL));
  326.  
  327.     /* font names and sizes */
  328.  
  329.     /* v4.4 supports user override of note and large date/title sizes */
  330.     nfsize       = (p = strrchr(notesfont, '/')) ? *p++ = '\0', atoi(p) :
  331.                 atoi(strrchr(NOTESFONT, '/') + 1);
  332.     dsize[LARGE] = (p = strrchr(datefont, '/')) ? *p++ = '\0', atoi(p) :
  333.                 atoi(strrchr(DATEFONT, '/') + 1);
  334.     tsize[LARGE] = (p = strrchr(titlefont, '/')) ? *p++ = '\0', atoi(p) :
  335.                 atoi(strrchr(TITLEFONT, '/') + 1);
  336.  
  337.     /* enlarge footer strings in whole-year/portrait mode */
  338.     if (do_whole_year && rotate == PORTRAIT)
  339.         fsize[MEDIUM] *= 1.25;
  340.  
  341.     /*
  342.      * if 8-bit remapping has been requested (-r flag), create new fonts
  343.      * with desired character remapping (Roman8 or Latin1, as requested)
  344.      */
  345.  
  346.     if (mapfonts != NOMAP) {
  347.  
  348.         /* include desired mapping */
  349.         GEN_PSCODE(mapfonts == ROMAN8 ? ps_roman8 : ps_latin1);
  350.  
  351.         /* boilerplate to remap for 8-bit fonts */
  352.         GEN_PSCODE(ps_remap);
  353.  
  354.         /* always generate code to remap title font */
  355.         PRT("/%s /%s%s remap_font\n", titlefont, titlefont, NEWFONT);
  356.         strcat(titlefont, NEWFONT);
  357.  
  358. #if MAP_DATEFONT    /* any text printed in date font (cf. pcaldefs.h)? */
  359.         /* generate code to remap date font if necessary */
  360.         PRT("FontDirectory /%s%s known not {\n", datefont, NEWFONT);
  361.         PRT("/%s /%s%s remap_font\n", datefont, datefont, NEWFONT);
  362.         PRT("} if\n");
  363.         strcat(datefont, NEWFONT);
  364. #endif
  365.  
  366.         /* generate code to remap notes font if necessary */
  367.         PRT("FontDirectory /%s%s known not {\n", notesfont, NEWFONT);
  368.         PRT("/%s /%s%s remap_font\n", notesfont, notesfont, NEWFONT);
  369.         PRT("} if\n");
  370.         strcat(notesfont, NEWFONT);
  371.     }
  372.  
  373.     /* define title, notes, and date fonts */
  374.     PRT("/titlefont /%s def\n", titlefont);
  375.     PRT("/datefont /%s def\n", datefont);
  376.     PRT("/notesfont /%s def\n", notesfont);
  377.  
  378.     /* typically defined in terms of above fonts - must define last */
  379.     PRT("/weekdayfont %s def\n", WEEKDAYFONT);
  380.     PRT("/footfont %s def\n", FOOTFONT);
  381.     PRT("/headingfont %s def\n", HEADINGFONT);
  382.  
  383.     /* print various font sizes and line/margin widths as PostScript arrays:
  384.      * one element for whole-year calendars; two (large, small) for single-
  385.      * month calendars
  386.      */
  387.  
  388.     if (do_whole_year) {
  389.         PRT("/datemargin [ %d ] def\n", dmargin[MEDIUM]);
  390.         PRT("/gridlinewidth [ %.1f ] def\n", gwidth[MEDIUM]);
  391.         PRT("/titlefontsize [ %d ] def\n", tsize[MEDIUM]);
  392.         PRT("/datefontsize [ %d ] def\n", dsize[MEDIUM]);
  393.     } else {
  394.         PRT("/datemargin [ %d %d ] def\n", dmargin[LARGE], dmargin[SMALL]);
  395.         PRT("/gridlinewidth [ %.1f %.1f ] def\n", gwidth[LARGE], gwidth[SMALL]);
  396.         PRT("/titlefontsize [ %d %d ] def\n", tsize[LARGE], tsize[SMALL]);
  397.         PRT("/datefontsize [ %d %d ] def\n", dsize[LARGE], dsize[SMALL]);
  398.     }
  399.  
  400.     dfltsize = do_whole_year ? MEDIUM : LARGE;
  401.  
  402.     PRT("/weekdayfontsize %d def\n", wsize[dfltsize]);
  403.     PRT("/footfontsize %d def\n", fsize[dfltsize]);
  404.     PRT("/notesfontsize %d def\n", nfsize);
  405.     PRT("/headingfontsize %d def\n", HEADINGFONTSIZE);
  406.  
  407.     /* pre-scale all fonts used by PostScript code; try to be smart about
  408.      * skipping those that we know (at this point) won't be needed (whole-
  409.      * year calendars use either 3 or 4 fonts, while single-month calendars 
  410.      * can use anywhere from 3 to 8).  "FF" et. al. are indices into the
  411.      * font array (cf pcalinit.ps) for the different font types.
  412.      */
  413.  
  414.     PRT("/FontFind { findfont exch scalefont } def\n");
  415.  
  416.     nfonts = 0;
  417.  
  418.     PRT("/FF %d def\n", nfonts);            /* footers */
  419.     if (FOOTSTRINGS())
  420.         ADDFONT("ff", calsize[dfltsize], "foot", FALSE);
  421.  
  422.     if (do_whole_year) {
  423.         PRT("/TF %d def\n", nfonts);        /* month/year title */
  424.         ADDFONT("tf", calsize[MEDIUM], "title", TRUE);
  425.  
  426.         PRT("/DF %d def\n", nfonts);        /* dates */
  427.         ADDFONT("df", calsize[MEDIUM], "date", TRUE);
  428.     } else {
  429.         PRT("/HF %d def\n", nfonts);        /* 'Notes' heading */
  430.         if (notes_hdr[0])
  431.             ADDFONT("hf", calsize[LARGE], "heading", FALSE);
  432.  
  433.         /* large/small (if used) scalings of the same font must be
  434.          * contiguous and appear in that order
  435.          */
  436.  
  437.         PRT("/TF %d def\n", nfonts);        /* large/small title */
  438.         ADDFONT("tf", calsize[LARGE], "title", TRUE);
  439.         if (small_cal_pos != SC_NONE)
  440.             ADDFONT("tf", calsize[SMALL], "title", TRUE);
  441.  
  442.         PRT("/DF %d def\n", nfonts);        /* large/small dates */
  443.         ADDFONT("df", calsize[LARGE], "date", TRUE);
  444.         if (small_cal_pos != SC_NONE)
  445.             ADDFONT("df", calsize[SMALL], "date", TRUE);
  446.  
  447.         PRT("/NF %d def\n", nfonts);        /* text in boxes */
  448.         ADDFONT("nf", calsize[LARGE], "notes", FALSE);
  449.     }
  450.  
  451.     PRT("/WF %d def\n", nfonts);            /* weekdays */
  452.     ADDFONT("wf", calsize[dfltsize], "weekday", FALSE);
  453.  
  454.     /* generate the font array (automatically in sync with above) */
  455.  
  456.     PRT("/allfonts [\n\t");
  457.     for (i = 0; i < nfonts; i++) {
  458.         PRT("%s ", allfonts[i]);
  459.         free(allfonts[i]);
  460.     }
  461.     PRT("\n] def\n");
  462.  
  463.     /*
  464.      * define various strings and numeric values used by Pcal
  465.      */
  466.  
  467.     /* month names */
  468.  
  469.     PRT("/month_names [");
  470.     for (i = JAN; i <= DEC; i++) {
  471.         PRT(i % 6 == 1 ? "\n\t" : " ");
  472.         (void) print_word(months[i-1]);
  473.     }
  474.     PRT(" ] def\n");
  475.  
  476.     /* day names - abbreviate if printing entire year on page */
  477.  
  478.     PRT("/day_names [");
  479.     for (i = SUN; i <= SAT; i++) {
  480.         PRT(i % 6 == 0 && ! do_whole_year ? "\n\t" : " ");
  481.         strcpy(tmp, days[(i + first_day_of_week) % 7]);
  482.         if (do_whole_year)
  483.             tmp[ABBR_DAY_LEN] = '\0';
  484.         (void) print_word(tmp);
  485.         }
  486.     PRT(" ] def\n");
  487.  
  488.     /* line separator */
  489.  
  490.     PRT("/linesep ");
  491.     print_word(LINE_SEP);
  492.     PRT(" def\n");
  493.  
  494.      /* rotation, scale, and translate values */
  495.  
  496.      PRT("/rval %d def\n", rotate);
  497.      PRT("/xsval %s def\n/ysval %s def\n", xsval, ysval);
  498.      PRT("/xtval %s def\n/ytval %s def\n", xtval, ytval);
  499.  
  500.     /* moon, Julian date, and box fill flags */
  501.  
  502.     PRT("/draw-moons %s def\n", cond[draw_moons]);
  503.     PRT("/julian-dates %s def\n", cond[julian_dates]);
  504.     PRT("/fill-boxes %s def\n", cond[! blank_boxes]);
  505.  
  506.     /* position of small calendars */
  507.  
  508.     PRT("/prev_small_cal %d def\n", prev_cal_box[small_cal_pos]);
  509.     PRT("/next_small_cal %d def\n", next_cal_box[small_cal_pos]);
  510.  
  511.     /* date and fill box shading values */
  512.  
  513.     strcpy(tmp, shading);
  514.     *(p = strchr(tmp, '/')) = '\0';
  515.     PRT("/dategray %s def\n", set_rgb(tmp));
  516.     PRT("/fillgray %s def\n", set_rgb(p + 1));
  517.     color_dates = strchr(tmp, RGB_CHAR) != NULL;
  518.  
  519.     /* PostScript boilerplate (part 1 of 1) */
  520.  
  521.     GEN_PSCODE(header);
  522.  
  523.     /* Additional PostScript code tailored to this calendar */
  524.  
  525.     GEN_PSCODE(color_dates ?
  526.             ps_prtday_rgb :        /* color prtday{} */
  527.             ps_prtday_bw);        /* B&W prtday{} */
  528.  
  529.     GEN_PSCODE(FOOTSTRINGS() ?
  530.             ps_footer :        /* at least one foot string */
  531.             ps_nofooter);        /* no foot strings */
  532.  
  533.     GEN_PSCODE(blank_boxes ?
  534.             ps_nofill :        /* blank fill boxes */
  535.             ps_fill);        /* shaded fill boxes */
  536.  
  537.     if (do_whole_year) {
  538.         GEN_PSCODE(rotate == LANDSCAPE ?
  539.                ps_year_l :        /* medium months (landscape) */
  540.                ps_year_p);        /* medium months (portrait)  */
  541.         GEN_PSCODE(ps_nojulians);    /* no julians or moons */
  542.         GEN_PSCODE(ps_nomoons);
  543.     } else {
  544.         GEN_PSCODE(julian_dates == NO_JULIANS ?
  545.                 ps_nojulians :        /* no julian dates */
  546.                 ps_julians);        /* some julian dates */
  547.         GEN_PSCODE(draw_moons == NO_MOONS ?
  548.                 ps_nomoons :        /* no moons */
  549.                 ps_moons);        /* some or all moons */
  550.         if (head)
  551.             GEN_PSCODE(ps_text);        /* date text */
  552.         GEN_PSCODE(ps_month);            /* single month */
  553.     }
  554.  
  555.     /*
  556.      * Write out PostScript code to print calendars
  557.      */
  558.  
  559.     for (this_month = month, this_year = year; nmonths--; ) {
  560.         print_month(this_month, this_year);
  561.         this_year = NEXT_YEAR(this_month, this_year);
  562.         this_month = NEXT_MONTH(this_month, this_year);
  563.     }
  564.  
  565.     /* generate trailer at end of PostScript output */
  566.     PS_TRAILER();
  567. }
  568.  
  569.  
  570. /*
  571.  * write_calfile - write dates in format suitable for Un*x "calendar" utility
  572.  * (and subsequent use by Pcal)
  573.  */
  574. void
  575. #ifdef PROTOS
  576. write_calfile(int month,        /* starting month   */
  577.           int year,            /* starting year    */
  578.           int nmonths)        /* number of months */
  579. #else
  580. write_calfile(month, year, nmonths)
  581.     int month            /* starting month   */;
  582.     int year;            /* starting year    */
  583.     int nmonths;            /* number of months */
  584. #endif
  585. {
  586.     KWD *k;
  587.  
  588.     /* look up the Pcal keywords (assumed present) for the -c output file */
  589.     for (k = keywds; k->name; k++) {
  590.         if (k->code == DT_NOTE) kw_note = k->name;
  591.         if (k->code == DT_OPT)  kw_opt  = k->name;
  592.         if (k->code == DT_YEAR) kw_year = k->name;
  593.     }
  594.  
  595.     /* print the date style for subsequent use by Pcal */
  596.     PRT("%s -%c\n", kw_opt, date_style == USA_DATES ? F_USA_DATES :
  597.                               F_EUR_DATES);
  598.  
  599.     for (this_month = month, this_year = year; nmonths--; ) {
  600.         print_dates(this_month, this_year);
  601.         this_year = NEXT_YEAR(this_month, this_year);
  602.         this_month = NEXT_MONTH(this_month, this_year);
  603.     }
  604. }
  605.  
  606. /*
  607.  * low-level utilities for PostScript generation
  608.  */
  609.  
  610. /*
  611.  * set_rgb - convert "<r>:<g>:<b>" to [r g b] or "<gray>" to [gray];
  612.  * return pointer to static buffer containing converted string
  613.  */
  614. char *
  615. #ifdef PROTOS
  616. set_rgb(char *s)
  617. #else
  618. set_rgb(s)
  619.     char *s;
  620. #endif
  621. {
  622.     static char buf[STRSIZ];
  623.     char *p1, *p2;
  624.     double val[3];
  625.     int n;
  626.  
  627.     val[0] = val[1] = val[2] = 0;        /* defaults */
  628.  
  629.     /* extract 1 - 3 floating-point values from string */
  630.     for (n = 1, p1 = s; n <= 3; n++, p1 = p2 + 1) {
  631.         val[n-1] = atof(p1);
  632.         if ((p2 = strchr(p1, RGB_CHAR)) == NULL)
  633.             break;
  634.     }
  635.  
  636.     /* single value is gray scale; assume anything else is [r g b] */
  637.     if (n > 1)
  638.         sprintf(buf, "[%.3f %.3f %.3f]", val[0], val[1], val[2]);
  639.     else
  640.         sprintf(buf, "[%.3f]", val[0]);
  641.  
  642.     return buf; 
  643. }
  644.  
  645. /*
  646.  * select_color - if the holiday color has not been explicitly selected,
  647.  * choose a color which contrasts with the majority of weekday colors
  648.  */
  649. int
  650. #ifdef PROTOS
  651. select_color(void)
  652. #else
  653. select_color()
  654. #endif
  655. {
  656.     int i, min, index;
  657.     char count[NUM_COLORS];
  658.  
  659.     for (i = 0; i < NUM_COLORS; i++)        /* clear counts */
  660.         count[i] = 0;
  661.  
  662.     for (i = SUN; i <= SAT; i++)            /* count colors */
  663.         count[day_color[i]]++;
  664.  
  665.     /* find smallest non-zero count; set its index and value */
  666.     for (i = 0, min = 99; i < NUM_COLORS; i++)
  667.         if (count[i] && count[i] < min)
  668.             min = count[index = i];
  669.  
  670.     /* return least-used color (or pick one if only one color used) */
  671.     return min == 7 ? index == BLACK ? GRAY : BLACK : index;
  672. }
  673.  
  674.  
  675. /*
  676.  * expand_fmt - expand a strftime-like date format specifier; pcal supports
  677.  * %[aAbBdjmUWyY] from strftime() plus %[luwDM] and prefixes [0o+-] (see
  678.  * below); places expanded string in output buffer and returns pointer to
  679.  * character following end of format specifier.  Assumes working date has
  680.  * been initialized (via RESET_DATE() macro) prior to first call for a given
  681.  * text string
  682.  */
  683. char *
  684. #ifdef PROTOS
  685. expand_fmt(char *buf,        /* output buffer (filled in)        */
  686.        char *p)        /* character following percent sign */
  687. #else
  688. expand_fmt(buf, p)
  689.     char *buf;        /* output buffer (filled in)        */
  690.     char *p;        /* character following percent sign */
  691. #endif
  692. {
  693.     char c;
  694.     static char *prefixes = "0o+-";
  695.     int firstday, wkday;
  696.     int adjust = 0, print_lz = FALSE, ordinal = FALSE, prev_num = -1;
  697.     int num_present = FALSE, num_value = 0;
  698.     DATE date;
  699.  
  700.     /* For compatibility with version 4.1, still support %[+-][bBdmY]
  701.      * (print the next/last month-name/day/month/year).  Version 4.11
  702.      * introduces %[+-]<n>[DWMY], which adjusts the working date by
  703.      * [+-]<n> days/weeks/months/years; this method is preferred due
  704.      * to its greater flexibility.
  705.      */
  706.  
  707.     buf[0] = '\0';        /* initialize output to null string */
  708.     
  709.     do {            /* loop until format character found */
  710.         switch (c = *p++) {
  711.         case 'a':    /* %a : abbreviated weekday */
  712.         case 'A':    /* %A : full weekday */
  713.             wkday = calc_weekday(work_month, work_day, work_year);
  714.             strcpy(buf, days[wkday]);
  715.             if (c == 'a')
  716.                 buf[ABBR_DAY_LEN] = '\0';
  717.             break;
  718.  
  719.         case 'b':    /* %b : abbreviated month name */
  720.         case 'B':    /* %B : full month name */
  721.             strcpy(buf, months[(work_month + adjust + 11) % 12]);
  722.             if (c == 'b')
  723.                 buf[ABBR_MONTH_LEN] = '\0';
  724.             break;
  725.  
  726.         case 'd':    /* %d : day of month (01-31) */
  727.             prev_num = work_day;
  728.             sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
  729.             break;
  730.  
  731.         case 'D':    /* %D : adjust working date by <N> days (NEW) */
  732.             if (!num_present || num_value == 0)
  733.                 RESET_DATE();
  734.             else
  735.                 SET_DATE(adjust * num_value);
  736.             break;
  737.  
  738.         case 'j':    /* %j : day of year (001-366) */
  739.             prev_num = DAY_OF_YEAR(work_month, work_day,
  740.                     work_year);
  741.             sprintf(buf, print_lz ? "%03d" : "%d", prev_num);
  742.             break;
  743.  
  744.         case 'l':    /* %l : days left in year (000-365) (NEW) */
  745.             prev_num = YEAR_LEN(work_year) - DAY_OF_YEAR(work_month,
  746.                     work_day, work_year);
  747.             sprintf(buf, print_lz ? "%03d" : "%d", prev_num);
  748.             break;
  749.  
  750.         case 'm':    /* %m : month (01-12) */
  751.             prev_num = (work_month + adjust + 11) % 12 + 1;
  752.             sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
  753.             break;
  754.  
  755.         case 'M':    /* %M : adjust date by <N> months (NEW) */
  756.             if (!num_present || num_value == 0)
  757.                 RESET_DATE();
  758.             else {
  759.                 int len;
  760.  
  761.                 work_month += adjust * num_value;
  762.                 while (work_month > DEC) {
  763.                     work_month -= 12;
  764.                     work_year++;
  765.                 }
  766.                 while (work_month < JAN) {
  767.                     work_month += 12;
  768.                     work_year--;
  769.                 }
  770.  
  771.                 /* make sure day of new month is legal */
  772.                 len = LENGTH_OF(work_month, work_year);
  773.                 if (work_day > len)
  774.                     work_day = len;
  775.             }
  776.             break;
  777.  
  778.         /* %u considers the week containing 1/1 to be week 1 and
  779.          * the next "logical Sunday" (the first day of the week
  780.          * as printed - cf. the -F option) to be the start of week
  781.          * 2; %U considers the first "logical Sunday" of the year to
  782.          * be the start of week 1.  %w and %W behave like %u and %U
  783.          * respectively, but use the first "logical Monday" instead.
  784.          */
  785.         case 'W':    /* %W : week number (00-53)       */
  786.             /* %W, if prefaced by [+-]N, adjusts the date by
  787.              * [+-]N weeks (resets if N == 0); check for this
  788.              * case first
  789.              */
  790.             if (num_present) {
  791.                 if (num_value == 0)    /* N = 0: reset date */
  792.                     RESET_DATE();
  793.                 else
  794.                     SET_DATE(7 * adjust * num_value);
  795.                 break;
  796.             }
  797.             /* fall through */
  798.         case 'u':    /* %u : week number (01-54) (NEW) */
  799.         case 'U':    /* %U : week number (00-53)       */
  800.         case 'w':    /* %w : week number (01-54) (NEW) */
  801.             firstday = ((TOLOWER(c) == 'w' ? 15 : 14) -
  802.                     START_BOX(JAN, work_year)) % 7 + 1;
  803.             prev_num = (DAY_OF_YEAR(work_month, work_day,
  804.                     work_year) - firstday + 7) / 7;
  805.             if (islower(c) && firstday != 1)
  806.                 prev_num++;
  807.             sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
  808.             break;
  809.  
  810.         case 'y':    /* %y : year w/o century (00-99) */
  811.             prev_num = (work_year + adjust) % 100;
  812.             sprintf(buf, "%02d", prev_num);
  813.             break;
  814.  
  815.         case 'Y':    /* %Y : year w/century */
  816.             /* %Y, if prefaced by [+-]N, adjusts the date by
  817.              * [+-]N years (resets if N == 0); check for this
  818.              * case first
  819.              */
  820.             if (num_present) {
  821.                 if (num_value == 0)    /* N = 0: reset date */
  822.                     RESET_DATE();
  823.                 else {
  824.                     int len;
  825.  
  826.                     work_year += adjust * num_value;
  827.  
  828.                     /* make sure day is legal */
  829.                     len = LENGTH_OF(work_month, work_year);
  830.                     if (work_day > len)
  831.                         work_day = len;
  832.                 }
  833.             } else {
  834.                 prev_num = work_year + adjust;
  835.                 sprintf(buf, "%d", prev_num);
  836.             }
  837.             break;
  838.  
  839.         /* prefix flags [o0+-] : set flags for next pass */
  840.  
  841.         case 'o':    /* %o : ordinal suffix (NEW) */
  842.             ordinal = TRUE;
  843.             break;
  844.  
  845.         case '0':    /* %0 : add leading zeroes (NEW) */
  846.             print_lz = TRUE;
  847.             break;
  848.  
  849.         case '+':    /* %+ : increment next value (NEW) */
  850.         case '-':    /* %- : decrement next value (NEW) */
  851.             adjust = c == '-' ? -1 : 1;
  852.             if (isdigit(*p)) {        /* get the number */
  853.                 num_present = TRUE;
  854.                 while (isdigit(*p))
  855.                     num_value = num_value * 10 +
  856.                             (*p++ - '0');
  857.             }
  858.             break;
  859.  
  860.         case '\0':    /* accidental end-of-string */
  861.         case ' ':
  862.             return p - 1;
  863.  
  864.         default:    /* other - just copy it to output */
  865.             sprintf(buf, "%c", c);
  866.             break;
  867.         };
  868.  
  869.     } while (strchr(prefixes, c) != NULL);
  870.  
  871.     /* append ordinal suffix if requested */
  872.     if (ordinal && prev_num >= 0)
  873.         strcat(buf, ordinal_suffix(prev_num));
  874.     return p;
  875.  
  876. }
  877.  
  878.  
  879. /*
  880.  * print_word - print a single word, representing punctuation and non-ASCII
  881.  * characters as octal literals and expanding format specifiers; return pointer
  882.  * to character following word (NULL if no word follows)
  883.  */
  884. char *
  885. #ifdef PROTOS
  886. print_word(char *p)
  887. #else
  888. print_word(p)
  889.     char *p;
  890. #endif
  891. {
  892.     char c, buf[STRSIZ];
  893.     int first = TRUE;    /* flag to avoid printing null strings */
  894.  
  895.     if (*p == '\0' || *(p += strspn(p, WHITESPACE)) == '\0')
  896.         return NULL;
  897.  
  898.     while ((c = *p) && !isspace(c & CHAR_MSK)) {
  899.         if (c == '%' && p[1] != '\0') {
  900.             p = expand_fmt(buf, p + 1);
  901.             if (*buf && first) {
  902.                 PRT("(");
  903.                 first = FALSE;
  904.             }
  905.             PUTSTR(isalnum, buf, stdout);    
  906.         } else {
  907.             if (first)
  908.                 PRT("(");
  909.             first = FALSE;
  910.             PUTCHAR(isalnum, c, stdout);
  911.             p++;
  912.         }
  913.     }
  914.  
  915.     if (!first)
  916.         PRT(")");
  917.  
  918.     return p;
  919. }
  920.  
  921.  
  922. /*
  923.  * print_db_word - debug version of print_word; omits parentheses, does not
  924.  * convert punctuation to escape sequences, and writes results to stderr
  925.  * (not stdout)
  926.  */
  927. char *
  928. #ifdef PROTOS
  929. print_db_word(char *p)
  930. #else
  931. print_db_word(p)
  932.     char *p;
  933. #endif
  934. {
  935.     char c, buf[STRSIZ];
  936.  
  937.     if (*p == '\0' || *(p += strspn(p, WHITESPACE)) == '\0')
  938.         return NULL;
  939.  
  940.     while ((c = *p) && !isspace(c & CHAR_MSK)) {
  941.         if (c == '%' && p[1] != '\0') {
  942.             p = expand_fmt(buf, p + 1);
  943.             PUTSTR(isprint, buf, stderr);
  944.         } else {
  945.             PUTCHAR(isprint, c, stderr);
  946.             p++;
  947.         }
  948.     }
  949.  
  950.     return p;
  951. }
  952.  
  953.  
  954. /*
  955.  * print_pstext - print tokens in text (assumed separated by single blank)
  956.  * in PostScript format and as debugging information if requested
  957.  */
  958. void
  959. #ifdef PROTOS
  960. print_pstext(char *p)
  961. #else
  962. print_pstext(p)
  963.     char *p;
  964. #endif
  965. {
  966.     char *s = p;        /* save for possible second pass */
  967.  
  968.     while (p = print_word(p))
  969.         PRT(" ");
  970.  
  971.     /* repeat to generate debugging info if requested */
  972.     if (debug_text)
  973.         while (s = print_db_word(s))
  974.             FPR(stderr, " ");
  975.  
  976. }
  977.  
  978.  
  979. /*
  980.  * print_text - print text as supplied; expand format specifiers but
  981.  * do not tokenize into words or translate punctuation to escape sequences
  982.  */
  983. void
  984. #ifdef PROTOS
  985. print_text(char *p)
  986. #else
  987. print_text(p)
  988.     char *p;
  989. #endif
  990. {
  991.     char c, buf[STRSIZ];
  992.  
  993.     while (c = *p)
  994.         if (c == '%' && p[1] != '\0') {
  995.             p = expand_fmt(buf, p + 1);
  996.             PUTSTR(isprint, buf, stdout);
  997.         } else {
  998.             PUTCHAR(isprint, c, stdout);
  999.             p++;
  1000.         }
  1001. }
  1002.  
  1003.  
  1004. /*
  1005.  * def_footstring - print definition for foot string, again converting 
  1006.  * all characters other than letters, digits, or space to octal escape
  1007.  * and expanding format specifiers
  1008.  */
  1009. void
  1010. #ifdef PROTOS
  1011. def_footstring(char *p,            /* definition */
  1012.            char *str)        /* name of string */
  1013. #else
  1014. def_footstring(p, str)
  1015.     char *p;            /* definition */
  1016.     char *str;            /* name of string */
  1017. #endif
  1018. {
  1019.     char c, buf[STRSIZ];
  1020.  
  1021.     this_day = 1;            /* set default day in foot string */
  1022.     RESET_DATE();            /* reset working date */
  1023.  
  1024.     PRT("/%s (", str);
  1025.     while (c = *p)
  1026.         if (c == '%' && p[1] != '\0') {
  1027.             p = expand_fmt(buf, p + 1);
  1028.             PUTSTR(isalnum, buf, stdout);
  1029.         } else {
  1030.             PUTCHAR(isalnum, c, stdout);
  1031.             p++;
  1032.         }
  1033.     PRT(") def\n");
  1034. }
  1035.  
  1036.  
  1037. /*
  1038.  * Routines to extract and print data
  1039.  */
  1040.  
  1041.  
  1042. /*
  1043.  * find_daytext - find and print the day (including notes) or holiday text
  1044.  * for the specified month/year
  1045.  */
  1046. void
  1047. #ifdef PROTOS
  1048. find_daytext(int month,
  1049.          int year,
  1050.          int is_holiday)    /* TRUE: print holiday text */
  1051. #else
  1052. find_daytext(month, year, is_holiday)
  1053.     int month, year;
  1054.     int is_holiday;        /* TRUE: print holiday text */
  1055. #endif
  1056. {
  1057.     register int day;
  1058.     year_info *py;
  1059.     month_info *pm;
  1060.     register day_info *pd;
  1061.     int first;
  1062.     char *fcn = is_holiday ? "holidaytext" : "daytext";
  1063.     char hol = is_holiday ? '*' : ' ';
  1064.  
  1065.     /* if no text for this year and month, return */
  1066.  
  1067.     if ((py = find_year(year, FALSE)) == NULL ||
  1068.         (pm = py->month[month-1]) == NULL)
  1069.         return;
  1070.  
  1071.     /* walk array of day text pointers and linked lists of text */
  1072.  
  1073.     for (day = 1; day <= LAST_NOTE_DAY; day++) {
  1074.         for (pd = pm->day[day-1], first = TRUE;
  1075.              pd;
  1076.              pd = pd->next) {
  1077.             if (pd->is_holiday != is_holiday)
  1078.                 continue;
  1079.             if (first) {
  1080.                 PRT("%d [ \n", day >= FIRST_NOTE_DAY ?
  1081.                     note_box(month, day, year) : day);
  1082.             }
  1083.             else {
  1084.                 PRT("\n");
  1085.                 print_word(LINE_SEP);    /* separate lines */
  1086.                 PRT("\n");
  1087.             }
  1088.             this_day = day >= FIRST_NOTE_DAY ? 1 : day;
  1089.             RESET_DATE();        /* reset working date */
  1090.             if (debug_text)
  1091.                 if (day < FIRST_NOTE_DAY)
  1092.                     FPR(stderr, "%02d/%02d/%d%c ", month,
  1093.                         day, year, hol);
  1094.                 else
  1095.                     FPR(stderr, "%02d[%02d]%d  ", month,
  1096.                         day - FIRST_NOTE_DAY + 1, year);
  1097.             print_pstext(pd->text);
  1098.             if (debug_text)
  1099.                 FPR(stderr, "\n");
  1100.             first = FALSE;
  1101.         }
  1102.         if (! first) {        /* wrap up call (if one made) */
  1103.             PRT("\n] %s\n", day >= FIRST_NOTE_DAY ? "notetext" :
  1104.                                 fcn);
  1105.         }
  1106.     }
  1107. }
  1108.  
  1109.  
  1110. /*
  1111.  * print_colors - print array specifying color of each date in current month
  1112.  * (formerly calculated on the fly in drawnums{} in pcalinit.ps)
  1113.  */
  1114. void
  1115. #ifdef PROTOS
  1116. print_colors(int month,
  1117.           int year)
  1118. #else
  1119. print_colors(month, year)
  1120.     int month, year;
  1121. #endif
  1122. {
  1123.     register int day;
  1124.     year_info *py;
  1125.     month_info *pm;
  1126.     unsigned long holidays;
  1127.     int i, j, len;
  1128.     short color[32];
  1129.  
  1130.     len = LENGTH_OF(month, year);
  1131.  
  1132.     /* determine each date's color from its day of the week */
  1133.     for (day = 1, j = FIRST_OF(month, year);
  1134.          day <= len;
  1135.          day++, j = (j + 1) % 7) {
  1136.         color[day] = day_color[j];
  1137.     }
  1138.  
  1139.     pm = (py = find_year(year, FALSE)) ? py->month[month-1] : NULL;
  1140.  
  1141.     /* override weekday color for holidays */
  1142.     for (holidays = pm ? pm->holidays : 0, day = 1;
  1143.          holidays;
  1144.          holidays >>= 1, day++)
  1145.         if (holidays & 01)
  1146.             color[day] = holiday_color;
  1147.  
  1148.     PRT("/date_color [ -1");        /* dummy value for element 0 */
  1149.     for (day = 1; day <= len; day++)
  1150.         PRT("%s %d", day % 10 == 1 ? " " : "", color[day]);
  1151.     PRT(" ] def\n");
  1152. }
  1153.  
  1154.  
  1155. /*
  1156.  * find_noteboxes - find and print the note box numbers for specified
  1157.  * month/year
  1158.  */
  1159. void
  1160. #ifdef PROTOS
  1161. find_noteboxes(int month,
  1162.            int year)
  1163. #else
  1164. find_noteboxes(month, year)
  1165.     int month, year;
  1166. #endif
  1167. {
  1168.     register int day;
  1169.     year_info *py;
  1170.     month_info *pm;
  1171.  
  1172.     /* if no text for this year and month, print empty list and return */
  1173.  
  1174.     if ((py = find_year(year, FALSE)) == NULL ||
  1175.         (pm = py->month[month-1]) == NULL) {
  1176.         PRT("/noteboxes [ ] def\n");
  1177.         return;
  1178.     }
  1179.  
  1180.     PRT("/noteboxes [");    /* start definition of list */
  1181.  
  1182.     /* walk array of note text pointers, converting days to box numbers */
  1183.  
  1184.     for (day = FIRST_NOTE_DAY; day <= LAST_NOTE_DAY; day++)
  1185.         if (pm->day[day-1])
  1186.             PRT(" %d", note_box(month, day, year));
  1187.     PRT(" ] def\n");
  1188.  
  1189. }
  1190.  
  1191.  
  1192. /*
  1193.  * print_dates - print the dates (in "calendar" format) for the specified
  1194.  * month and year
  1195.  */
  1196. void
  1197. #ifdef PROTOS
  1198. print_dates(int month,
  1199.         int year)
  1200. #else
  1201. print_dates(month, year)
  1202.     int month, year;
  1203. #endif
  1204. {
  1205.     register int day;
  1206.     year_info *py;
  1207.     month_info *pm;
  1208.     register day_info *pd;
  1209.     unsigned long holidays;
  1210.     int has_holiday_text;
  1211.     static int first = TRUE;
  1212.     static int save_year = 0;
  1213.  
  1214.     /* if no text for this year and month, return */
  1215.  
  1216.     if ((py = find_year(year, FALSE)) == NULL ||
  1217.         (pm = py->month[month-1]) == NULL)
  1218.         return;
  1219.  
  1220.     /* print the year if it has changed */
  1221.  
  1222.     if (year != save_year)
  1223.         PRT("%s %d\n", kw_year, save_year = year);
  1224.  
  1225.     /* walk array of day text pointers and linked lists of text */
  1226.  
  1227.     for (holidays = pm->holidays, day = 1;
  1228.          day < FIRST_NOTE_DAY;
  1229.          holidays >>= 1, day++) {
  1230.         has_holiday_text = FALSE;
  1231.         for (pd = pm->day[day-1]; pd; pd = pd->next) {
  1232.             if (date_style == USA_DATES)
  1233.                 PRT("%02d/%02d", month, day);
  1234.             else
  1235.                 PRT("%02d/%02d", day, month);
  1236.             PRT(pd->is_holiday ? "* " : " ");
  1237.             this_day = day;
  1238.             RESET_DATE();    /* reset working date */
  1239.             print_text(pd->text);
  1240.             PRT("\n");
  1241.             has_holiday_text |= pd->is_holiday;
  1242.         }
  1243.         /* was date flagged as holiday w/o associated text? */
  1244.         if ((holidays & 01) && !has_holiday_text) {
  1245.             if (date_style == USA_DATES)
  1246.                 PRT("%02d/%02d*\n", month, day);
  1247.             else
  1248.                 PRT("%02d/%02d*\n", day, month);
  1249.         }
  1250.     }
  1251. }
  1252.  
  1253.  
  1254.  
  1255. /*
  1256.  * print_moon_info - print the information necessary to draw moons.  If
  1257.  * printing moons on all days, print the phase for each day; if printing
  1258.  * only quarter moons, tweak the phase to an exact quarter (so the icon
  1259.  * is printed correctly) and generate a list of the quarter-moon dates
  1260.  */
  1261. void
  1262. #ifdef PROTOS
  1263. print_moon_info(int month,
  1264.             int year)
  1265. #else
  1266. print_moon_info(month, year)
  1267.     int month, year;
  1268. #endif
  1269. {
  1270.     int n, ndays, day, quarter;
  1271.     char *p;
  1272.     unsigned long qdays;
  1273.     double phase;
  1274.     static char *q[4] = {"NM", "1Q", "FM", "3Q"};
  1275.  
  1276.     if (draw_moons == NO_MOONS)
  1277.         return;
  1278.  
  1279.     /* print the phase of the moon for each day of the month */
  1280.  
  1281.     PRT("/moon_phases [\t\t%% from ");
  1282.     if ((p = find_moonfile(year)) != NULL)
  1283.         PRT ("%s", p);
  1284.     else {
  1285.         PRT("algorithm");
  1286.         if (atof(time_zone) != 0.0)
  1287.             PRT(" (UTC offset = %s)", time_zone);
  1288.     }
  1289.     PRT("\n\t");
  1290.  
  1291.     for (n = 0, qdays = 0L, day = 1, ndays = LENGTH_OF(month, year);
  1292.          day <= ndays;
  1293.          day++) {
  1294.         phase = find_phase(month, day, year, &quarter);
  1295.         if (DEBUG(DEBUG_MOON))
  1296.             FPR(stderr, "%02d/%02d/%d: %.3f %s\n", month, day,
  1297.                 year, phase, quarter != MOON_OTHER ?
  1298.                 q[quarter] : "");
  1299.         /* adjust phase to exact quarter if printing only quarters */
  1300.         if (draw_moons == SOME_MOONS && quarter != MOON_OTHER)
  1301.             phase = 0.25 * quarter;
  1302.         if (draw_moons == ALL_MOONS || quarter != MOON_OTHER)
  1303.             PRT("%.3f%s", PRT_TWEAK(phase), ++n % 10 == 0 ?
  1304.                 "\n\t" : " ");
  1305.         if (quarter != MOON_OTHER)
  1306.             qdays |= 1L << (day - 1);
  1307.     }
  1308.     PRT("] def\n");
  1309.  
  1310.     /* if drawing only quarter moons, print days when they occur */
  1311.  
  1312.     if (draw_moons == SOME_MOONS) {
  1313.         PRT("/quarter_moons [ ");
  1314.         for (day = 1; qdays; day++, qdays >>= 1)
  1315.             if (qdays & 01)
  1316.                 PRT("%d ", day);
  1317.         PRT("] def\n");
  1318.     }
  1319. }
  1320.  
  1321.  
  1322. /*
  1323.  * print_julian_info - print the information necessary to print Julian dates
  1324.  */
  1325. void
  1326. #ifdef PROTOS
  1327. print_julian_info(int month,
  1328.           int year)
  1329. #else
  1330. print_julian_info(month, year)
  1331.     int month, year;
  1332. #endif
  1333. {
  1334.  
  1335.     if (julian_dates != NO_JULIANS)
  1336.         PRT("/jdstart %d def\n", DAY_OF_YEAR(month, 1, year));
  1337.     if (julian_dates == ALL_JULIANS)
  1338.         PRT("/yearlen %d def\n", YEAR_LEN(year));
  1339. }
  1340.  
  1341.  
  1342. /*
  1343.  * print_month - generate calendar for specified month/year
  1344.  */
  1345. void
  1346. #ifdef PROTOS
  1347. print_month(int month,
  1348.         int year)
  1349. #else
  1350. print_month(month, year)
  1351.     int month, year;
  1352. #endif
  1353. {
  1354.     static int nmonths = 0, page = 0;
  1355.     int startbox;
  1356.  
  1357.     /* start of new physical page? */
  1358.     if (!do_whole_year || nmonths % 12 == 0)
  1359.         PS_STARTPAGE();
  1360.  
  1361.     PRT("/year %d def\n", year);        /* set up year and month */
  1362.     PRT("/month %d def\n", month);
  1363.  
  1364.     /* move starting box to second row if conflict with small calendars */
  1365.     startbox = START_BOX(month, year);
  1366.     if (!do_whole_year &&
  1367.         (prev_cal_box[small_cal_pos] == startbox ||
  1368.          next_cal_box[small_cal_pos] == startbox) )
  1369.         startbox += 7;
  1370.     PRT("/startbox %d def\n", startbox);
  1371.     PRT("/ndays %d def\n", LENGTH_OF(month, year));
  1372.  
  1373.     find_noteboxes(month, year);        /* make list of note boxes */
  1374.     print_colors(month, year);        /* make list of date colors */
  1375.  
  1376.     /* are we printing 12 months per page or only one? */
  1377.  
  1378.     if (do_whole_year) {
  1379.         /* reset foot strings at start of each page */
  1380.         if (nmonths % 12 == 0) {
  1381.             def_footstring(lfoot, "Lfootstring");
  1382.             def_footstring(cfoot, "Cfootstring");
  1383.             def_footstring(rfoot, "Rfootstring");
  1384.             def_footstring(notes_hdr, "notesheading");
  1385.         }
  1386.  
  1387.         PRT("/posn %d def\n", nmonths % 12);    /* location on page */
  1388.         PRT("printmonth\n");
  1389.     }
  1390.     else {
  1391.         /* reset foot strings each month (may change) */
  1392.         def_footstring(lfoot, "Lfootstring");
  1393.         def_footstring(cfoot, "Cfootstring");
  1394.         def_footstring(rfoot, "Rfootstring");
  1395.         def_footstring(notes_hdr, "notesheading");
  1396.  
  1397.         /* generate information necessary for small calendars */
  1398.  
  1399.         if (small_cal_pos != SC_NONE) {
  1400.             int m, y;
  1401.  
  1402.             PRT("/p_year %d def\n", y = PREV_YEAR(month, year));
  1403.             PRT("/p_month %d def\n", m = PREV_MONTH(month, year));
  1404.             PRT("/p_startbox %d def\n", START_BOX(m, y));
  1405.             PRT("/p_ndays %d def\n", LENGTH_OF(m, y));
  1406.  
  1407.             PRT("/n_year %d def\n", y = NEXT_YEAR(month, year));
  1408.             PRT("/n_month %d def\n", m = NEXT_MONTH(month, year));
  1409.             PRT("/n_startbox %d def\n", START_BOX(m, y));
  1410.             PRT("/n_ndays %d def\n", LENGTH_OF(m, y));
  1411.         }
  1412.  
  1413.         print_julian_info(month, year);        /* Julian date info */
  1414.         print_moon_info(month, year);        /* moon info */
  1415.  
  1416.         PRT("printmonth\n");
  1417.         find_daytext(month, year, TRUE);    /* holiday text */
  1418.         find_daytext(month, year, FALSE);    /* day and note text */
  1419.     }
  1420.  
  1421.     /* end of physical page? */
  1422.     if (!do_whole_year || ++nmonths % 12 == 0)
  1423.         PS_ENDPAGE();
  1424. }
  1425.